/**************************************************************************************************
* Title: SumPlane_16f.inl
* Author: Gael Huber
* Description: Describes functions for 16-byte aligned plane
**************************************************************************************************/

/**************************************************************************************************
* Default constructor
**************************************************************************************************/
inline Plane_16f::Plane_16f(void) 
	: v(_mm_setzero_ps())
{ }

/**************************************************************************************************
* Constructor given vector
**************************************************************************************************/
inline Plane_16f::Plane_16f(const __m128& f)
	: v(f)
{ }

/**************************************************************************************************
* Constructor from array
**************************************************************************************************/
inline Plane_16f::Plane_16f(const float* f)
	: v(_mm_load_ps(f))
{ }

/**************************************************************************************************
* Constructor given specific values for a, b, c, and d
**************************************************************************************************/
inline Plane_16f::Plane_16f(float a, float b, float c, float d)
	: v(_mm_set_ps(d, c, b, a))
{ }

/**************************************************************************************************
* Copy constructor
**************************************************************************************************/
inline Plane_16f::Plane_16f(const Plane_16f& rhs)
	: v(rhs.v)
{ }

/**************************************************************************************************
* Destructor
**************************************************************************************************/
inline Plane_16f::~Plane_16f(void)
{ }

/**************************************************************************************************
* Cast to float array
**************************************************************************************************/
inline Plane_16f::operator float*(void) {
	return (float*) this;
}

/**************************************************************************************************
* Cast to const float array
**************************************************************************************************/
inline Plane_16f::operator const float*(void) const {
	return (const float*) this;
}

/**************************************************************************************************
* Assignment operator
**************************************************************************************************/
inline Plane_16f& Plane_16f::operator=(const Plane_16f& rhs) {
	if(this == &rhs)
		return *this;

	v = rhs.v;
	return *this;
}

/**************************************************************************************************
* Scale the plane by a float
**************************************************************************************************/
inline Plane_16f& Plane_16f::operator*=(float rhs) {
	v = _mm_mul_ps(v, _mm_load1_ps(&rhs));
	return *this;
}

/**************************************************************************************************
* Scale the plane by a vector
**************************************************************************************************/
inline Plane_16f& Plane_16f::operator*=(const __m128& rhs) {
	v = _mm_mul_ps(v, rhs);
	return *this;
}

/**************************************************************************************************
* Divide the plane by a scalar
**************************************************************************************************/
inline Plane_16f& Plane_16f::operator/=(float rhs) {
	v = _mm_div_ps(v, _mm_load1_ps(&rhs));
	return *this;
}

/**************************************************************************************************
* Divide the plane by a scalar
**************************************************************************************************/
inline Plane_16f& Plane_16f::operator/=(const __m128& rhs) {
	v = _mm_div_ps(v, rhs);
	return *this;
}

/**************************************************************************************************
* Return an unmodified copy of the plane
**************************************************************************************************/
inline Plane_16f Plane_16f::operator+(void) const {
	return *this;
}

/**************************************************************************************************
* Return a negated copy of the plane
**************************************************************************************************/
inline Plane_16f Plane_16f::operator-(void) const {
	return Plane_16f(_mm_mul_ps(v, _mm_set1_ps(-1.0f)));
}

/**************************************************************************************************
* Returns a copy of the plane multiplied by a scalar
**************************************************************************************************/
inline Plane_16f Plane_16f::operator*(float rhs) const {
	return Plane_16f(_mm_mul_ps(v, _mm_load1_ps(&rhs)));
}

/**************************************************************************************************
* Returns a copy of the plane multiplied by a scalar
**************************************************************************************************/
inline Plane_16f Plane_16f::operator*(const __m128& rhs) const {
	return Plane_16f(_mm_mul_ps(v, rhs));
}

/**************************************************************************************************
* Returns a copy of the plane divided by a scalar
**************************************************************************************************/
inline Plane_16f Plane_16f::operator/(float rhs) const {
	return Plane_16f(_mm_div_ps(v, _mm_load1_ps(&rhs)));
}

/**************************************************************************************************
* Returns a copy of the plane divided by a scalar
**************************************************************************************************/
inline Plane_16f Plane_16f::operator/(const __m128& rhs) const {
	return Plane_16f(_mm_div_ps(v, rhs));
}

/**************************************************************************************************
* Returns a copy of the plane multiplied by a scalar
**************************************************************************************************/
inline Plane_16f operator*(float lhs, const Plane_16f& rhs) {
	return Plane_16f(_mm_mul_ps(_mm_load1_ps(&lhs), rhs.v));
}

/**************************************************************************************************
* Returns a copy of the plane multiplied by a scalar
**************************************************************************************************/
inline Plane_16f operator*(const __m128& lhs, const Plane_16f& rhs) {
	return Plane_16f(_mm_mul_ps(lhs, rhs.v));
}

/**************************************************************************************************
* Equivalence
**************************************************************************************************/
inline bool Plane_16f::operator==(const Plane_16f& rhs) const {
	return _mm_movemask_ps(_mm_cmpeq_ps(v, rhs.v)) == 0x0F;
}

/**************************************************************************************************
* Non-equivalence
**************************************************************************************************/
inline bool Plane_16f::operator!=(const Plane_16f& rhs) const {
	return _mm_movemask_ps(_mm_cmpeq_ps(v, rhs.v)) != 0x0F;
}

/**************************************************************************************************
* Dot product between the plane and a vector. ax + by + cz + dw
**************************************************************************************************/
inline float PlaneDot_16f(const Plane_16f* pP, const Vector4_16f* pV) {
	__m128 m(_mm_mul_ps(pP->v, pV->v));
	return m.m128_f32[0] + m.m128_f32[1] + m.m128_f32[2] + m.m128_f32[3];
}

/**************************************************************************************************
* Return the dot product of a coordinate with the plane. ax + by + cz + d
**************************************************************************************************/
inline float PlaneDotCoord_16f(const Plane_16f* pP, const Vector3_16f* pV) {
	__m128 m(_mm_mul_ps(pP->v, pV->v));
	return m.m128_f32[0] + m.m128_f32[1] + m.m128_f32[2] + pP->d;
}

/**************************************************************************************************
* ax + by + cz
**************************************************************************************************/
inline float PlaneDotNormal_16f(const Plane_16f* pP, const Vector3_16f* pV) {
	__m128 m(_mm_mul_ps(pP->v, pV->v));
	return m.m128_f32[0] + m.m128_f32[1] + m.m128_f32[2];
}

/**************************************************************************************************
* Scale the plane by a factor s
**************************************************************************************************/
inline Plane_16f* PlaneScale_16f(Plane_16f* pOut, const Plane_16f* pP, float s) {
	pOut->v = _mm_mul_ps(pP->v, _mm_load1_ps(&s));
	return pOut;
}

/**************************************************************************************************
* Scale the plane by a factor of vector s
**************************************************************************************************/
inline Plane_16f* PlaneScale_16f(Plane_16f* pOut, const Plane_16f* pP, const __m128& s) {
	pOut->v = _mm_mul_ps(pP->v, s);
	return pOut;
}